#include "cmath.h"
#include "cintmath.h"

/**
 * sgn - signum function
 * @param num
 * @return -1 if "num" < 0, 1 if "num" > 0, 0 if "num" = 0
 */
template<typename T> T CMath<T>::sgn(T num) {
  return num / CMath<T>::abs(num);
}
//---------------------------------------------------------------------------

/**
 * degToRad - converts degrees to radians
 * @param angle angle in degrees
 * @return angle in radians
 */
template<> double CMath<double>::degToRad(double angle) {
  return (angle / 180) * PI;
}
//---------------------------------------------------------------------------

/**
 * degToRad - converts degrees to radians
 * @param angle angle in degrees
 * @return angle in radians
 */
template<> float CMath<float>::degToRad(float angle) {
  return (angle / 180) * PI;
}
//---------------------------------------------------------------------------

/**
 * radToDeg - converts radians to degrees
 * @param angle angle in radians
 * @return angle in degrees
 */
template<> double CMath<double>::radToDeg(double angle) {
  return (angle / PI) * 180;
}
//---------------------------------------------------------------------------

/**
 * radToDeg - converts radians to degrees
 * @param angle angle in radians
 * @return angle in degrees
 */
template<> float CMath<float>::radToDeg(float angle) {
  return (angle / PI) * 180;
}
//---------------------------------------------------------------------------

/**
 * min - returns minimum from given numbers
 * @param n1
 * @param n2
 * @return minimum
 */
template<typename T> T CMath<T>::min(T n1, T n2) {
  return n1 < n2 ? n1 : n2;
}
//---------------------------------------------------------------------------

/**
 * max - returns maximum from given numbers
 * @param n1
 * @param n2
 * @return maximum
 */
template<typename T> T CMath<T>::max(T n1, T n2) {
  return n1 > n2 ? n1 : n2;
}
//---------------------------------------------------------------------------

/**
 * abs - returns absolute value of given number
 * @param num
 * @return absolute value of "num"
 */
template<typename T> T CMath<T>::abs(T num) {
  return num < 0 ? -num : num;
}
//---------------------------------------------------------------------------

/**
 * abs - returns absolute value of given number
 * @param num
 * @return absolute value of "num"
 */
template<> unsigned char CMath<unsigned char>::abs(unsigned char num) {
  return 1;
}
//---------------------------------------------------------------------------

/**
 * abs - returns absolute value of given number
 * @param num
 * @return absolute value of "num"
 */
template<> unsigned short CMath<unsigned short>::abs(unsigned short num) {
  return 1;
}
//---------------------------------------------------------------------------

/**
 * abs - returns absolute value of given number
 * @param num
 * @return absolute value of "num"
 */
template<> unsigned int CMath<unsigned int>::abs(unsigned int num) {
  return 1;
}
//---------------------------------------------------------------------------

/**
 * abs - returns absolute value of given number
 * @param num
 * @return absolute value of "num"
 */
template<> unsigned long CMath<unsigned long>::abs(unsigned long num) {
  return 1;
}
//---------------------------------------------------------------------------

/**
 * sqrt - returns square root of given number
 * @param num
 * @return square root of "num"
 */
template<typename T> T CMath<T>::sqrt(T num) {
  return (T) ::sqrt(num);
}
//---------------------------------------------------------------------------

/**
 * sqrt - returns square root of given number
 * @param num
 * @return square root of "num"
 */
template<> CBigInt CMath<CBigInt>::sqrt(CBigInt num) {

  // Temporary variables
  CBigInt tmp(num / 2), tmp2;

  // Add precision while number is integer
  do {
    tmp2 = tmp;
    tmp = (tmp + num / tmp) / 2;
  } while (tmp != tmp2);
  return tmp;
}
//---------------------------------------------------------------------------

/**
 * sqrt - returns square root of given number
 * @param num
 * @return square root of "num"
 */
template<> CFraction CMath<CFraction>::sqrt(CFraction num) {
  double n(::sqrt(num.getNumerator())), d(::sqrt(num.getDenominator()));
  return n == (long) n && d == (long) d ? CFraction((long) n, (long) d)
      : CMath<double>::toFraction(n / d);
}
//---------------------------------------------------------------------------

/**
 * log2 - returns logarithm with base 2 from given number
 * @param num
 * @return logarithm with base 2
 */
template<typename T> T CMath<T>::log2(T num) {
  return log2(num);
}
//---------------------------------------------------------------------------

/**
 * log2 - returns logarithm with base 2 from given number
 * @param num
 * @return logarithm with base 2
 */
template<> CBigInt CMath<CBigInt>::log2(CBigInt num) {

  // Integer value of log2 from arbitrary number is number of it's valid
  // digits before decimal point in binary number system minus one

  // In binary number system would be number too long, so we calculate number
  // of valid digits in 256 number system and multiply result with 8
  CBigInt count(0);
  while (num > 256) {
    num /= 256;
    count += 8;
  }

  // Remainder after dividing number by 256 we can transfer to binary number
  // system and calculate number of remaining digits
  return count + num.toNumberSystem(2).getNumOfDigits() - 1;
}
//---------------------------------------------------------------------------

/**
 * log2 - returns logarithm with base 2 from given number
 * @param num
 * @return logarithm with base 2
 */
template<> CFraction CMath<CFraction>::log2(CFraction num) {

  // log2(a/b) = log2(a) - log2(b)
  return CFraction(log2(num.getNumerator()) - log2(num.getDenominator()));
}
//---------------------------------------------------------------------------

/**
 * pow - raises given base to given exponent
 * @param base base
 * @param exp exponent
 * @return "base" raised to "exp"
 */
template<typename T> T CMath<T>::pow(T base, double exp) {
  return (T) ::pow(base, exp);
}
//---------------------------------------------------------------------------

/**
 * pow - raises given base to given exponent
 * @param base base
 * @param exp exponent
 * @return "base" raised to "exp"
 */
template<> CBigInt CMath<CBigInt>::pow(CBigInt base, double exp) {
  return CIntMath<CBigInt>::pow(base, CBigInt((long) exp));
}
//---------------------------------------------------------------------------

/**
 * pow - raises given base to given exponent
 * @param base base
 * @param exp exponent
 * @return "base" raised to "exp"
 */
template<> CFraction CMath<CFraction>::pow(CFraction base, double exp) {
  double n(::pow(base.getNumerator(), exp));
  double d(::pow(base.getDenominator(), exp));
  return n == (long) n && d == (long) d ? CFraction((long) n, (long) d)
      : CMath<double>::toFraction(n / d);
}
//---------------------------------------------------------------------------

/**
 * equals - check whether two numbers are equal
 * @param n1
 * @param n2
 * @return true if "n1" equals to "n2"
 */
template<typename T> bool CMath<T>::equals(T n1, T n2, T epsilon) {
  return abs(n1 - n2) < epsilon;
}
//---------------------------------------------------------------------------

/**
 * equals - check whether two numbers are equal
 * @param n1
 * @param n2
 * @return true if "n1" equals to "n2"
 */
template<typename T> bool CMath<T>::equals(T n1, T n2) {
  return n1 == n2;
}
//---------------------------------------------------------------------------

/**
 * equals - check whether two numbers are equal
 * @param n1
 * @param n2
 * @return true if "n1" equals to "n2"
  */
template<> bool CMath<float>::equals(float n1, float n2) {
  return equals(n1, n2, EPSILON);
}
//---------------------------------------------------------------------------

/**
 * equals - check whether two numbers are equal
 * @param n1
 * @param n2
 * @return true if "n1" equals to "n2"
 */
template<> bool CMath<double>::equals(double n1, double n2) {
  return equals(n1, n2, EPSILON);
}
//---------------------------------------------------------------------------

/**
 * compare - compares two numbers
 * @param n1
 * @param n2
 * @return COMPARE_GREATER (n1 > n2), COMPARE_LESSER (n1 < n2), COMPARE_EQUAL
 */
template<typename T> int CMath<T>::compare(T n1, T n2, T epsilon) {
  T tmp(n1 - n2);
  if (tmp > epsilon) return COMPARE_GREATER;
  if (tmp < -epsilon) return COMPARE_LESSER;
  return COMPARE_EQUAL;
}
//---------------------------------------------------------------------------

/**
 * compare - compares two numbers
 * @param n1
 * @param n2
 * @return COMPARE_GREATER (n1 > n2), COMPARE_LESSER (n1 < n2), COMPARE_EQUAL
 */
template<typename T> int CMath<T>::compare(T n1, T n2) {
  return compare(n1, n2, T(0));
}
//---------------------------------------------------------------------------

/**
 * compare - compares two numbers
 * @param n1
 * @param n2
 * @return COMPARE_GREATER (n1 > n2), COMPARE_LESSER (n1 < n2), COMPARE_EQUAL
 */
template<> int CMath<float>::compare(float n1, float n2) {
  return compare(n1, n2, EPSILON);
}
//---------------------------------------------------------------------------

/**
 * compare - compares two numbers
 * @param n1
 * @param n2
 * @return COMPARE_GREATER (n1 > n2), COMPARE_LESSER (n1 < n2), COMPARE_EQUAL
 */
template<> int CMath<double>::compare(double n1, double n2) {
  return compare(n1, n2, EPSILON);
}
//---------------------------------------------------------------------------

/**
 * doubleToFraction - converts real number to fraction
 * @param value
 * @param precision
 * @return fraction
 */
template<typename T> CFraction CMath<T>::doubleToFraction(double value,
    unsigned char precision) {

  // If "value" is not a valid real number, return zero fraction
  if (isnan(value)) return CFraction(0);

  // Convert given real number "value" to string
  string tmp = CMath<double>::toString(value);

  // If decimal point is not present, given number is integer => only
  // numerator will be set
  size_t decPointPos = tmp.find(".");
  if (decPointPos == string::npos) decPointPos = tmp.find(",");
  if (decPointPos == string::npos) return CFraction((long) value);

  // Create result from part of number before decimal point and save part
  // after decimal point to temporary variable "tmp"
  CFraction result;
  try {
    result = CMath<long>::parse(tmp.substr(0, decPointPos));
  } catch (CNumberParseException e) {
  }
  tmp = tmp.substr(decPointPos + 1);

  // Iterate through letters (digits) of "tmp" create fractions and add them
  // to result
  unsigned char i = 0, maxD = precision <= tmp.size() ? precision
      : tmp.size();
  long j = 10;
  for (; i < maxD; i++, j *= 10) {
    result += CFraction((long) (tmp[i] - '0'), j);
  }

  // Round remainder and add it to resulting fraction
  if (++i < tmp.size()) {
    result += CFraction((long) round((double) (tmp[i] - '0') / 10), j);
  }
  return result;
}
//---------------------------------------------------------------------------

/**
 * parse - converts string to number
 * @param str
 * @return number
 * @throws CNumberParseException
 */
template<typename T> T CMath<T>::parse(string str)
    throw(CNumberParseException) {
  T tmp;
  istringstream stream(str);
  if (stream >> tmp) return tmp;
  else throw CNumberParseException(getClassName(), "parse");
}
//---------------------------------------------------------------------------

/**
 * parse - converts string to number
 * @param str
 * @return number
 * @throws CNumberParseException
 */
template<> char CMath<char>::parse(string str) throw(CNumberParseException) {

  // char je nutne parsovat jako short aby nedochazelo k zamene se znaky
  return (char) CMath<short>::parse(str);
}
//---------------------------------------------------------------------------

/**
 * parse - converts string to number
 * @param str
 * @return number
 * @throws CNumberParseException
 */
template<> unsigned char CMath<unsigned char>::parse(string str)
    throw(CNumberParseException) {

  // unsigned char must be parsed as short
  return (unsigned char) CMath<short>::parse(str);
}
//---------------------------------------------------------------------------

/**
 * parse - converts string to fraction
 * @param str
 * @return fraction
 * @throws CNumberParseException
 */
template<> CFraction CMath<CFraction>::parse(string str)
    throw(CNumberParseException) {

  // Explode string by "/" and save numerator and denominator
  size_t pos = str.find("/");
  return CFraction(CMath<long>::parse(str.substr(0, pos)),
      CMath<long>::parse(str.substr(pos + 1)));
}
//---------------------------------------------------------------------------

/**
 * parse - converts string to big integer number
 * @param str
 * @return big integer number
 * @throws CNumberParseException
 */
template<> CBigInt CMath<CBigInt>::parse(string str)
    throw(CNumberParseException) {
  return CBigInt(str);
}
//---------------------------------------------------------------------------

/**
 * parseHex - converts string to hexadecimal number
 * @param str
 * @return hexadecimal number
 * @throws CNumberParseException
 */
template<typename T> long CMath<T>::parseHex(string str)
    throw(CNumberParseException) {

  // Zero
  long tmp(0x0);

  // Conversion digit by digit
  for (TIndex i = 2; i < str.length(); i++) {
    tmp *= 0x10;
    switch (tolower(str[i])) {
      case '0' :
        tmp += 0x0;
        break;
      case '1' :
        tmp += 0x1;
        break;
      case '2' :
        tmp += 0x2;
        break;
      case '3' :
        tmp += 0x3;
        break;
      case '4' :
        tmp += 0x4;
        break;
      case '5' :
        tmp += 0x5;
        break;
      case '6' :
        tmp += 0x6;
        break;
      case '7' :
        tmp += 0x7;
        break;
      case '8' :
        tmp += 0x8;
        break;
      case '9' :
        tmp += 0x9;
        break;
      case 'a' :
        tmp += 0xa;
        break;
      case 'b' :
        tmp += 0xb;
        break;
      case 'c' :
        tmp += 0xc;
        break;
      case 'd' :
        tmp += 0xd;
        break;
      case 'e' :
        tmp += 0xe;
        break;
      case 'f' :
        tmp += 0xf;
        break;
      default :
        throw CNumberParseException(getClassName(), "parseHex");
        break;
    }
  }
  return tmp;
}
//---------------------------------------------------------------------------

/**
 * toString - converts number to string
 * @param num
 * @return string
 */
template<typename T> string CMath<T>::toString(T num) {
  ostringstream stream;
  stream << num;
  return stream.str();
}
//---------------------------------------------------------------------------

/**
 * toInt - converts given number to int
 * @param num
 * @return int number
 */
template<typename T> int CMath<T>::toInt(T num) {
  return (int) num;
}
//---------------------------------------------------------------------------

/**
 * toInt - converts given fraction to int
 * @param num
 * @return int number
 */
template<> int CMath<CFraction>::toInt(CFraction num) {
  return (int) num.getNumerator() / num.getDenominator();
}
//---------------------------------------------------------------------------

/**
 * toInt - converts given big integer to int
 * @param num
 * @return int number
 */
template<> int CMath<CBigInt>::toInt(CBigInt num) {
  return (int) num.longValue();
}
//---------------------------------------------------------------------------

/**
 * toLong - converts given number to long
 * @param num
 * @return long integer number
 */
template<typename T> long CMath<T>::toLong(T num) {
  return (long) num;
}
//---------------------------------------------------------------------------

/**
 * toLong - converts given fraction to long
 * @param num
 * @return long integer number
 */
template<> long CMath<CFraction>::toLong(CFraction num) {
  return (long) num.getNumerator() / num.getDenominator();
}
//---------------------------------------------------------------------------

/**
 * toLong - converts given big integer number to long
 * @param num
 * @return long integer number
 */
template<> long CMath<CBigInt>::toLong(CBigInt num) {
  return num.longValue();
}
//---------------------------------------------------------------------------

/**
 * toShort - converts given number to short
 * @param num
 * @return short number
 */
template<typename T> short CMath<T>::toShort(T num) {
  return (short) num;
}
//---------------------------------------------------------------------------

/**
 * toShort - converts given fraction to short
 * @param num
 * @return short number
 */
template<> short CMath<CFraction>::toShort(CFraction num) {
  return (short) num.getNumerator() / num.getDenominator();
}
//---------------------------------------------------------------------------

/**
 * toShort - converts given big integer number to short
 * @param num
 * @return short number
 */
template<> short CMath<CBigInt>::toShort(CBigInt num) {
  return (short) num.longValue();
}
//---------------------------------------------------------------------------

/**
 * toFloat - converts given number to float
 * @param num
 * @return real number
 */
template<typename T> float CMath<T>::toFloat(T num) {
  return (float) num;
}
//---------------------------------------------------------------------------

/**
 * toFloat - converts given fraction to float
 * @param num
 * @return real number
 */
template<> float CMath<CFraction>::toFloat(CFraction num) {
  return (float) num.getNumerator() / num.getDenominator();
}
//---------------------------------------------------------------------------

/**
 * toFloat - converts given big integer number to float
 * @param num
 * @return real number
 */
template<> float CMath<CBigInt>::toFloat(CBigInt num) {
  return (float) num.longValue();
}
//---------------------------------------------------------------------------

/**
 * toDouble - converts given number to double
 * @param num
 * @return real number
 */
template<typename T> double CMath<T>::toDouble(T num) {
  return (double) num;
}
//---------------------------------------------------------------------------

/**
 * toDouble - converts given fraction to double
 * @param num
 * @return real number
 */
template<> double CMath<CFraction>::toDouble(CFraction num) {
  return (double) num.getNumerator() / num.getDenominator();
}
//---------------------------------------------------------------------------

/**
 * toDouble - converts given big integer number to double
 * @param num
 * @return real number
 */
template<> double CMath<CBigInt>::toDouble(CBigInt num) {
  return (double) num.longValue();
}
//---------------------------------------------------------------------------

/**
 * toBigInt - converts given number to big integer number
 * @param num
 * @return big integer number
 */
template<typename T> CBigInt CMath<T>::toBigInt(T num) {
  return CBigInt((long) num);
}
//---------------------------------------------------------------------------

/**
 * toBigInt - converts given fraction to big integer number
 * @param num
 * @return big integer number
 */
template<> CBigInt CMath<CFraction>::toBigInt(CFraction num) {
  return CBigInt((long) num.getNumerator() / num.getDenominator());
}
//---------------------------------------------------------------------------

/**
 * toBigInt - converts given number to big integer number (compatibility)
 * @param num
 * @return big integer number
 */
template<> CBigInt CMath<CBigInt>::toBigInt(CBigInt num) {
  return num;
}
//---------------------------------------------------------------------------

/**
 * toFraction - converts given number to fraction
 * @param num
 * @return fraction
 */
template<typename T> CFraction CMath<T>::toFraction(T num) {
  return doubleToFraction((double) num, MAX_DECIMAL_DIGITS_DOUBLE);
}
//---------------------------------------------------------------------------

/**
 * toFraction - converts given fraction to fraction (compatibility)
 * @param num
 * @return fraction
 */
template<> CFraction CMath<CFraction>::toFraction(CFraction num) {
  return num;
}
//---------------------------------------------------------------------------

/**
 * toFraction - converts given big integer number to fraction
 * @param num
 * @return fraction
 */
template<> CFraction CMath<CBigInt>::toFraction(CBigInt num) {
  return CFraction(num.longValue());
}
//---------------------------------------------------------------------------

template class CMath<char>;
template class CMath<short>;
template class CMath<int>;
template class CMath<long>;
template class CMath<unsigned char>;
template class CMath<unsigned short>;
template class CMath<unsigned int>;
template class CMath<unsigned long>;
template class CMath<float>;
template class CMath<double>;
template class CMath<CBigInt>;
template class CMath<CFraction>;
